﻿using AiMachineControl.Common;
using AiMachineControl.IService;
using AiMachineControl.Model;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.FileProviders;
using System.Diagnostics;
using System.IO;

namespace AiMachineControl.Service
{
    /// <summary>
    /// 命令处理操作服务
    /// </summary>
    public class CommandProcessService:ICommandProcessService
    {
        private readonly IAppsettingService _appsettingService;
        private readonly IFileService _fileService;
        private readonly IFileProvider _fileProvider;
        private readonly GlobalService _globalService;
        public CommandProcessService(IAppsettingService configService, IFileService fileService, IFileProvider fileProvider, GlobalService globalService)
        {
            _appsettingService = configService;
            _fileService = fileService;
            _fileProvider = fileProvider;
            _globalService = globalService;
        }

        /// <summary>
        /// 离线渲染,直接通过选定的模型渲染
        /// </summary>
        /// <param name="AiType">渲染类型</param>
        /// <param name="UseLogId">算力机使用记录</param>
        /// <param name="OffLineInfo">渲染类型</param>
        /// <param name="aiSoftDosArr">调用命令</param>
        /// <param name="Message">返回消息</param>
        public bool StartOfflineProject(string AiType, int UseLogId, OffLineInfo offLineInfo, string aiSoftDosArr, out string Message)
        {
            Console.WriteLine("离线渲染类型:" + AiType);
            DateTime startTime = DateTime.Now;
            Process process = new Process();
            //设置进程的启动信息  
            process.StartInfo.FileName = "python";
            aiSoftDosArr = aiSoftDosArr.Replace("{id}", offLineInfo.Id.ToString()).Replace("{type}", AiType).Replace("{name}", offLineInfo.ModelName).Replace("{trialfolder}", offLineInfo.TrialFolder)
                .Replace("{result}", offLineInfo.ResultName).Replace("{file}", offLineInfo.FileAddress);
            process.StartInfo.Arguments = aiSoftDosArr;

            OffLineReback reback = new OffLineReback()
            {
                Id = offLineInfo.Id,
                Index = offLineInfo.Index,
                FilePath = ""
            };
            process.StartInfo.UseShellExecute = false;  
            process.StartInfo.RedirectStandardOutput = true;  
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardError = true;                                               
            process.StartInfo.CreateNoWindow = true;

            process.OutputDataReceived += (sender, e) => {
                if (e != null && !string.IsNullOrEmpty(e.Data))
                {
                    Console.WriteLine(e.Data);
                    if (e.Data.Contains("end offline rendering"))
                    {
                        string fileaddress = e.Data.Replace("end offline rendering:", "");
                        reback.FilePath = fileaddress;
                        Console.WriteLine("The process is stop...");
                        Console.WriteLine("OffLineReback:" + System.Text.Json.JsonSerializer.Serialize<OffLineReback>(reback, MetadataOnlyOffLineRebackOnlyContext.Default.OffLineReback));
                        Console.WriteLine("推理后的视频路径为：" + fileaddress);
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.OfflinFinish,
                            UseLogId = UseLogId,
                            StatusCode = 0,
                            Message = "该离线视频已处理完成",
                            Data = reback
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                    if (e.Data.Contains("process end=="))
                    {
                        Console.WriteLine("更新算力机状态为空闲..");
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.MachineClose,
                            StatusCode = 0,
                            UseLogId = UseLogId,
                            Message = "请将当前算力机设置为空闲状态",
                            Data = DateTime.Now.ToString()
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                        Console.WriteLine("离线渲染完成:" + offLineInfo.Index + ",耗时:" + (DateTime.Now - startTime).TotalSeconds);
                        //process.Kill();
                    }
                }
            };
            Console.WriteLine("准备进程启动");

            Message = "";
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            process.WaitForExit();
            Message = "正在离线渲染";
            return true;
        }

        /// <summary>
        /// 根据项目版本号和本地文件匹配，如果一致，直接启动，不一致先下载最新的模型文件至本地，再启动
        /// </summary>
        /// <param name="tUseLogId">算力机使用记录</param>
        /// <param name="AiType">模型类型</param>
        /// <param name="proName">项目名</param>
        /// <param name="VersionNo">模型项目版本号</param>
        /// <param name="IsFirstRun">是否初次运行</param>
        /// <param name="aiSoftDosArr">调用命令</param>
        /// <param name="Message">返回消息</param>
        public bool ReadingProjectInfo(int tUseLogId,int AiType, string proName, string VersionNo, bool IsFirstRun, string aiSoftDosArr, out string Message)
        {
            Console.WriteLine("Reading living:" + AiType);
            if (IsFirstRun)//只会存在用户第一次使用时：版本号为空，直接从模型文件服务器下载该项目初始模型和最新版本的文件
            {
                InitModelFile(AiType, proName);
                UpdateProjectInfo(AiType, proName, VersionNo); //版本升级至最新
            }
            else
            {
                UpdateProjectInfo(AiType, proName, VersionNo); //版本升级至最新
            }

            Process process = new Process();
            // 设置进程的启动信息  
            process.StartInfo.FileName = "python";
            process.StartInfo.Arguments = aiSoftDosArr.Replace("{modelname}", proName);//string.Format("ser.py DataModel/{0}/data/ --workspace DataModel/{0}/trial/trial_{0}_torso/ -O --torso --test --test_train --preload 2 --data_range 0 1000", proName);
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.CreateNoWindow = true;
            process.OutputDataReceived += (sender, e) => {
                if (e != null && !string.IsNullOrEmpty(e.Data))
                {
                    Console.WriteLine(e.Data);
                    if (e.Data.Contains("Preload audio model fine"))
                    {
                        Console.WriteLine("The process has started...");
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.MachineStart,
                            UseLogId = tUseLogId,
                            StatusCode = 0,
                            Message = "The process has started...",
                            Data = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                    if (e.Data.Contains("Connection reset by peer"))//运行异常
                    {
                        Console.WriteLine("The process is stop...");
                        process.Kill();
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.MachineError,
                            UseLogId = tUseLogId,
                            StatusCode = 0,
                            Message = "The process is stop...",
                            Data = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                }
            };
            Console.WriteLine("准备进程启动");

            Message = "";
            process.Start(); 
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            process.WaitForExit();
            Message = "软件已启动完成，可开始推流";
            return true;
        }

        /// <summary>
        /// 拆分音频数据
        /// </summary>
        /// <param name="tUseLogId">算力机使用记录</param>
        /// <param name="avfId">数字人Id</param>
        /// <param name="proName">项目名</param>
        /// <param name="file">wav文件地址</param>
        /// <param name="aiSoftDos">调用命令</param>
        public bool SpliteWavs(int tUseLogId, int avfId, string proName, string file, string aiSoftDos)
        {
            Console.WriteLine("Reading Split WavToNpy...");
            Process process = new Process();
            // 设置进程的启动信息  

            process.StartInfo.FileName = "python";
            process.StartInfo.Arguments = aiSoftDos.Replace("{name}", proName).Replace("{file}", file);
            Console.WriteLine(process.StartInfo.Arguments);
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.CreateNoWindow = true;
            process.OutputDataReceived += (sender, e) =>
            {
                if (e != null && !string.IsNullOrEmpty(e.Data))
                {
                    Console.WriteLine(e.Data);
                    if (e.Data.Contains("start split wavs"))
                    {
                        Console.WriteLine("The process has started...");
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.MachineStart,
                            UseLogId = tUseLogId,
                            StatusCode = 0,
                            Message = "The process has started...",
                            Data = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                    if (e.Data.Contains("npylist:"))//运行结束
                    {
                        string npylist = e.Data.Replace("npylist:", "");
                        Console.WriteLine("The process is stop...");
                        Console.WriteLine("输出值为：" + npylist);
                        VoiceInfo voiceInfo = new VoiceInfo();
                        voiceInfo.Id = avfId;
                        voiceInfo.FileAdd = file;
                        voiceInfo.ModelName = proName;
                        voiceInfo.NpyList = npylist.TrimEnd(',').Split(',').ToList<string>();
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.SplitEnd,
                            UseLogId = tUseLogId,
                            StatusCode = 0,
                            Message = "The process is stop...",
                            Data = voiceInfo
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                    if (e.Data.Contains("end split wavs"))
                    {
                        Console.WriteLine("The process is finish...");
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.MachineClose,
                            UseLogId = tUseLogId,
                            StatusCode = 0,
                            Message = "请将当前算力机设置为空闲状态",
                            Data = DateTime.Now.ToString()
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                }
            };
            Console.WriteLine("准备进程启动");
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            if (process.WaitForExit(1000))
            {
                Console.WriteLine("Task is finished");
            }
            return true;
        }

        /// <summary>
        /// 合成视频数据
        /// </summary>
        /// <param name="tUseLogId">算力机使用记录</param>
        /// <param name="proName">项目名</param>
        /// <param name="combinfo">合成信息</param>
        /// <param name="aiSoftDos">调用命令</param>
        public bool CombineVideo(int tUseLogId, string proName, VideoCombine combinfo, string aiSoftDos)
        {
            Console.WriteLine("Reading Split WavToNpy...");
            OffLineReback reback = new OffLineReback()
            {
                Id = combinfo.Id,
                Index = combinfo.Index,
                FilePath = ""
            };
            Process process = new Process();
            // 设置进程的启动信息  
            process.StartInfo.FileName = "python";
            process.StartInfo.Arguments = aiSoftDos.Replace("{name}", proName).Replace("{type}", combinfo.Type.ToString()).Replace("{picvideo}", combinfo.FileOne).Replace("{video}", combinfo.FileTwo)
                .Replace("{pvfbl}", combinfo.OneFBL).Replace("{vfbl}", combinfo.TwoFBL).Replace("{left}", combinfo.Left).Replace("{top}", combinfo.Top);
            Console.WriteLine(process.StartInfo.Arguments);
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.CreateNoWindow = true;
            process.OutputDataReceived += (sender, e) =>
            {
                if (e != null && !string.IsNullOrEmpty(e.Data))
                {
                    Console.WriteLine(e.Data);
                    if (e.Data.Contains("start combine file"))
                    {
                        Console.WriteLine("The process has started...");
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.MachineStart,
                            UseLogId = tUseLogId,
                            StatusCode = 0,
                            Message = "The process has started...",
                            Data = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                    if (e.Data.Contains("combine file:"))//运行结束
                    {
                        string filePath = e.Data.Replace("combine file:", "");
                        reback.FilePath = filePath;
                        Console.WriteLine("The process is stop...");
                        Console.WriteLine("输出值为：" + filePath);
                        
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.VideoCombine,
                            UseLogId = tUseLogId,
                            StatusCode = 0,
                            Message = "The process is stop...",
                            Data = reback
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                    if (e.Data.Contains("combine file finish"))
                    {
                        Console.WriteLine("The process is finish...");
                        SoketMessage sendSoketMsg = new SoketMessage()
                        {
                            SendClientId = _appsettingService.GetValue(new string[] { "MachineInfo", "Id" }),
                            Action = (int)EnumModel.ActionTypeEnum.MachineClose,
                            StatusCode = 0,
                            Message = "请将当前算力机设置为空闲状态",
                            Data = DateTime.Now.ToString()
                        };
                        HostedService.SendSocketMsg(sendSoketMsg);
                    }
                }
            };
            Console.WriteLine("开始启动进程");
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            if (process.WaitForExit(1000))
            {
                Console.WriteLine("Task is finished");
            }
            return true;
        }

        /// <summary>
        /// 更新本算力机指定项目，更新至最新版本
        /// </summary>
        /// <param name="AiType">模型类型</param>
        /// <param name="proName">项目名</param>
        /// <param name="VersionNo">模型项目版本号</param>
        public bool UpdateProjectInfo(int AiType, string proName, string VersionNo)
        {
            try
            {
                string AiModelServerUrl = _appsettingService.GetAppsetting("AiModelServerUrl");//模型文件服务器地址
                string AiModelUpdateInterfaceUrl = _appsettingService.GetAppsetting("AiModelUpdateInterfaceUrl");//模型文件获取接口地址
                string AiModelFolder = _appsettingService.GetAppsetting("AiModelFolder");//模型文件本地保存文件夹地址
                var directory = new DirectoryInfo(AiModelFolder);
                DirectoryInfo[] dir = directory.GetDirectories();
                if (dir.Count() > 0)//本机存在项目
                {
                    DirectoryInfo targetFile = dir.FirstOrDefault(p => p.Name.StartsWith(proName));
                    if (targetFile != null)
                    {
                        string filePath = AiModelFolder + "/" + proName + "/" + "version.txt";
                        // 读取文件内容  
                        string[] lines = File.ReadAllLines(filePath);
                        string tAiType = AiType.ToString();
                        string tproName = targetFile.Name; 
                        string tVersionNo = lines[0].Replace("version", "");
                        string newSaveUrl = AiModelFolder;
                        //通过接口获取更新文件下载地址
                        string returnval = RequestHelper.Get(AiModelServerUrl + AiModelUpdateInterfaceUrl + "?type=" + tAiType + "&proName=" + tproName + "&versionno=" + tVersionNo);
                        string[] ravals = returnval.Split("@@");
                        InterfaceMachine machine = new InterfaceMachine();
                        if (ravals.Length > 1)
                        {
                            machine.Version = ravals[0].Replace("\"", "");
                            machine.FileAddress = ravals[1].Replace("\"", "");
                        }
                        else
                        {
                            return false;
                        }
                        if (!string.IsNullOrEmpty(machine.FileAddress))
                        {
                            string newFileName = ""; 
                            string fileAdress = _fileService.FileDownSave(machine.FileAddress, newSaveUrl, newFileName);
                            newSaveUrl = string.Format("{0}/{1}", newSaveUrl, newFileName); 
                            string filename = FileHelper.GetFileName(fileAdress);
                            //如果上传的文件为压缩文件，就解压缩
                            if (FileHelper.GetFileFullExt(fileAdress) == ".zip" || FileHelper.GetFileFullExt(fileAdress) == ".tar" || FileHelper.GetFileFullExt(fileAdress) == ".7z")
                            {
                                FileHelper.UnZip(newSaveUrl, fileAdress);
                                //解压后删除下载的压缩文件
                                FileHelper.DeleteFile(newSaveUrl + "/" + filename, true);
                            }
                        }
                        lines[0] = "version" + machine.Version;
                        // 将修改后的内容写回文件  
                        File.WriteAllLines(filePath, lines);
                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("更新项目模型[" + proName + "]报错：" + ex.Message);
                return false;
            }
            return false;
        }

        /// <summary>
        /// 初始化本地模型文件(加载模型的第一个版本)
        /// </summary>
        /// <param name="AiType">模型类型</param>
        /// <param name="proName">项目名</param>
        private void InitModelFile(int AiType, string proName)
        {
            string AiModelServerUrl = _appsettingService.GetAppsetting("AiModelServerUrl");//模型文件服务器地址
            string AiModelInterfaceUrl = _appsettingService.GetAppsetting("AiModelInterfaceUrl");//模型文件获取接口地址
            //通过接口获取文件下载地址
            try
            {
                string returnval = RequestHelper.Get(AiModelServerUrl + AiModelInterfaceUrl + "?type=" + AiType + "&proName=" + proName);
                Console.WriteLine("**********:" + returnval);
                string[] ravals = returnval.Split("@@");
                InterfaceMachine machine = new InterfaceMachine();
                if (ravals.Length > 1)
                {
                    machine.Version = ravals[0].Replace("\"", "");
                    machine.FileAddress = ravals[1].Replace("\"", "");
                }
                else
                {
                    return;
                }
                if (!string.IsNullOrEmpty(machine.FileAddress))
                {
                    machine.FileAddress = machine.FileAddress.Replace("\"", "");
                    string newSaveUrl = _appsettingService.GetAppsetting("AiModelFolder");//模型文件本地保存文件夹地址
                    string newFileName = "";
                    string fileAdress = _fileService.FileDownSave(machine.FileAddress, newSaveUrl, "");
                    Console.WriteLine("**********:"+fileAdress);
                    newSaveUrl = string.Format("{0}/{1}", newSaveUrl, newFileName);
                    string filename = FileHelper.GetFileName(fileAdress);
                    //如果上传的文件为压缩文件，就解压缩
                    if (FileHelper.GetFileFullExt(fileAdress) == ".zip" || FileHelper.GetFileFullExt(fileAdress) == ".tar" || FileHelper.GetFileFullExt(fileAdress) == ".7z")
                    {
                        FileHelper.UnZip3(newSaveUrl, fileAdress);
                        Console.WriteLine("UmZip Successfull");
                        //解压后删除下载的压缩文件
                        FileHelper.DeleteFile(newSaveUrl + "/" + filename, true);
                    }
                    string txtfilePath = newSaveUrl + "/" + proName + "/" + "version.txt";
                    // 判断文件是否存在  
                    if (!File.Exists(txtfilePath))
                    {
                        // 文件不存在，创建并添加内容  
                        using (StreamWriter sw = File.CreateText(txtfilePath))
                        {
                            sw.WriteLine("version" + machine.Version);
                        }
                    }
                    else
                    {
                        // 读取文件内容  
                        string[] lines = File.ReadAllLines(txtfilePath);
                        // 修改文件中的值
                        lines[0] = "version" + machine.Version;
                        // 将修改后的内容写回文件  
                        File.WriteAllLines(txtfilePath, lines);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("初始化项目模型[" + proName + "]报错：" + ex.Message);
            }
        }
    }
}
